/**
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2007，所有权利保留。
 * 
 * 项目名：	rumba
 * 文件名：	BMscbMgr.java
 * 模块说明：	
 * 修改历史：
 * Oct 31, 2007 - lxm - 创建。
 */
package com.hd123.rumba.mscb.order.biz;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.sf.hibernate.HibernateException;

import com.hd123.rumba.domain.CDomain;
import com.hd123.rumba.domain.IDomain;
import com.hd123.rumba.domain.IDomainMgr;
import com.hd123.rumba.exception.BusinessException;
import com.hd123.rumba.exception.NoneException;
import com.hd123.rumba.log.RumbaLogger;
import com.hd123.rumba.mscb.constraint.ConstraintFactory;
import com.hd123.rumba.mscb.constraint.IConstraint;
import com.hd123.rumba.mscb.order.CMscbCancelOrder;
import com.hd123.rumba.mscb.order.CMscbOrder;
import com.hd123.rumba.mscb.order.IMscbCancelOrder;
import com.hd123.rumba.mscb.order.IMscbCancelOrderMgr;
import com.hd123.rumba.mscb.order.IMscbOrder;
import com.hd123.rumba.mscb.order.IMscbOrderMgr;
import com.hd123.rumba.mscb.order.IMscbOrderPrompt;
import com.hd123.rumba.mscb.subscriber.CSubscriber;
import com.hd123.rumba.mscb.subscriber.ISubscriberMgr;
import com.hd123.rumba.mscb.subscriber.Subscriber;
import com.hd123.rumba.mscb.subscriber.SubscriberMgrFactory;
import com.hd123.rumba.prompt.provider.IPromptContent;
import com.hd123.rumba.prompt.provider.IPromptProvider;
import com.hd123.rumba.prompt.provider.PromptProviderFactory;
import com.hd123.rumba.runtime.IManager;
import com.hd123.rumba.runtime.NamingMgr;
import com.hd123.rumba.runtime.OperateContext;
import com.hd123.rumba.runtime.biz.BManager;
import com.hd123.rumba.runtime.biz.BPersistent;
import com.hd123.rumba.util.ClassifiedHashtable;
import com.hd123.rumba.util.CollectionMatcher;
import com.hd123.rumba.util.ElementMatcher;

/**
 * 消息订阅管理员 | 业务层.维护 | 管理员 | 实现类
 * 
 * @author lxm
 * 
 */
public class BMscbMgr extends BManager implements IBMscbMgr {

  private static RumbaLogger logger = RumbaLogger.getLogger(BMscbMgr.class);

  @Override
  public IManager getPerzMgr() throws BusinessException {
    return this.getOrdMgr();
  }

  public BMscbOrder createOrder(String domainUuid) throws BusinessException, HibernateException {
    this.checkParameterNotNull(domainUuid, "domainUuid");
    BMscbOrder biz = new BMscbOrder();
    IDomain domain = this.getDmMgr().getValidDomain(domainUuid);
    if (domain == null)
      throw new BusinessException("指定的" + CDomain.CAPTION_CLASS + "\"" + domainUuid
          + "\"找不到，或者不可用。");
    IMscbOrder perz = this.getOrdMgr().createOrder(domain);
    biz.readFrom(perz);
    return biz;
  }

  public BMscbOrder loadOrder(String uuid) throws BusinessException, HibernateException {
    IMscbOrder perz = this.getOrdMgr().getOrder(uuid);
    if (perz != null) {
      BMscbOrder biz = new BMscbOrder();
      biz.readFrom(perz);
      return biz;
    } else
      return null;
  }

  public ClassifiedHashtable saveOrder(BMscbOrder order, OperateContext operate)
      throws BusinessException, HibernateException {
    this.checkParameterNotNull(order, "order");
    this.checkParameterNotNull(operate, "operate");

    IDomain domain = this.getDmMgr().getValidDomain(order.getDomain().getUuid());
    IMscbOrder perz = null;
    if (order.getUuid() == null)
      perz = this.getOrdMgr().createOrder(domain);
    else
      perz = this.getOrdMgr().getOrder(order.getUuid());

    ClassifiedHashtable h = this.verifyToSave(order, perz);
    if (h != null)
      return h;

    this.writeToOrder(order, perz);
    this.getOrdMgr().saveOrder(perz, operate);
    order.readFrom(perz);

    return null;
  }

  public ClassifiedHashtable cancelOrder(BMscbOrder cancelledOrder, Subscriber subscriber,
      OperateContext operate) throws BusinessException, HibernateException {
    this.checkParameterNotNull(cancelledOrder, "cancelledOrder");
    this.checkParameterNotNull(subscriber, "subscriber");
    this.checkParameterNotNull(operate, "operate");

    IMscbOrder perzCOrder = this.getOrdMgr().getOrder(cancelledOrder.getUuid());
    if (perzCOrder == null)
      throw new BusinessException("找不到指定的" + CMscbOrder.CAPTION_CLASS + "。");
    ClassifiedHashtable h = this.verifyToCancel(cancelledOrder, perzCOrder);
    if (h != null)
      return h;

    Subscriber ordSub = cancelledOrder.getSubscriber();
    if (!subscriber.getMgrClassName().equals(ordSub.getMgrClassName()))
      throw new BusinessException("指定的subscriber与cancelledOrder不匹配。");

    if (subscriber.equals(ordSub))
      this.deleteOrder(perzCOrder, operate);
    else if (CSubscriber.VALUE_TYPE_ROLE.equals(ordSub.getType_())
        && CSubscriber.VALUE_TYPE_USER.equals(subscriber.getType_())) {
      ISubscriberMgr subMgr = SubscriberMgrFactory.buildMgr(subscriber.getMgrClassName());
      if (subMgr.relative(ordSub, subscriber))
        this.createAndSaveCancelOrder(perzCOrder, subscriber, operate);
    }
    return null;
  }

  public <T extends BPersistent> T getOrder(String uuid, Class<T> bizClass)
      throws BusinessException, HibernateException {
    this.checkParameterNotNull(bizClass, "bizClass");
    IMscbOrder perz = this.getOrdMgr().getOrder(uuid);
    if (perz != null) {
      T biz = this.createBizClass(bizClass);
      biz.readFrom(perz);
      return biz;
    } else
      return null;
  }

  public <T extends BPersistent> T getValidOrder(String uuid, Class<T> bizClass)
      throws BusinessException, HibernateException {
    this.checkParameterNotNull(bizClass, "bizClass");
    IMscbOrder perz = this.getOrdMgr().getValidOrder(uuid);
    if (perz != null) {
      T biz = this.createBizClass(bizClass);
      biz.readFrom(perz);
      return biz;
    } else
      return null;
  }

  public <T extends BPersistent> T getCancelOrder(String uuid, Class<T> bizClass)
      throws BusinessException, HibernateException {
    this.checkParameterNotNull(bizClass, "bizClass");
    IMscbCancelOrder perz = this.getCOrdMgr().getOrder(uuid);
    if (perz != null) {
      T biz = this.createBizClass(bizClass);
      biz.readFrom(perz);
      return biz;
    } else
      return null;
  }

  public <T extends BPersistent> T getValidCancelOrder(String uuid, Class<T> bizClass)
      throws BusinessException, HibernateException {
    this.checkParameterNotNull(bizClass, "bizClass");
    IMscbCancelOrder perz = this.getCOrdMgr().getValidOrder(uuid);
    if (perz != null) {
      T biz = this.createBizClass(bizClass);
      biz.readFrom(perz);
      return biz;
    } else
      return null;
  }

  public <T extends BPersistent> List<T> getCancelOrdersByOrder(String orderUuid, Class<T> bizClass)
      throws BusinessException, HibernateException {
    this.checkParameterNotNull(bizClass, "bizClass");
    IMscbOrder perzOrder = getOrdMgr().getOrder(orderUuid);
    List<T> bizList = new ArrayList<T>();
    if (perzOrder == null)
      return bizList;
    List<IMscbCancelOrder> perzList = getCOrdMgr().getOrdersByCancelled(perzOrder);
    for (IMscbCancelOrder perzCOrder : perzList) {
      T bizCOrder = this.createBizClass(bizClass);
      bizCOrder.readFrom(perzCOrder);
      bizList.add(bizCOrder);
    }
    return bizList;
  }

  public <T extends BPersistent> T getValidCancelOrderByKey(String orderUuid,
      Subscriber subscriber, Class<T> bizClass) throws BusinessException, HibernateException {
    this.checkParameterNotNull(subscriber, "subscriber");
    this.checkParameterNotNull(bizClass, "bizClass");
    IMscbOrder perzOrder = getOrdMgr().getOrder(orderUuid);
    if (perzOrder == null)
      return null;
    IMscbCancelOrder perzCOrder = getCOrdMgr().getValidOrderByKey(perzOrder, subscriber);
    T bizCOrder = this.createBizClass(bizClass);
    bizCOrder.readFrom(perzCOrder);
    return bizCOrder;
  }

  public void notifyT(String domainUuid, String subjectClass, String event,
      Map<String, Object> dict, IPromptContent pmptCtx, OperateContext operate)
      throws BusinessException, HibernateException {
    this.checkParameterNotNull(subjectClass, "subjectClass");
    this.checkParameterNotNull(event, "event");
    this.checkParameterNotNull(pmptCtx, "pmptCtx");
    this.checkParameterNotNull(operate, "operate");
    Map<String, Object> aDict = dict == null ? new HashMap() : dict;

    String logCtx = this.getLogContext(domainUuid, subjectClass, event);
    logger.debug("触发……(" + logCtx + ")");

    try {
      IDomain domain = null;
      if (domainUuid != null) {
        domain = getDmMgr().getValidDomain(domainUuid);
        if (domain == null)
          throw new BusinessException("指定的" + CDomain.CAPTION_CLASS + "\"" + domainUuid
              + "\"找不到，或者不可用。");
      }

      logger.debug("搜索订阅单……(" + logCtx + ")");
      List<IMscbOrder> orders = getOrdMgr().getValidOrdersBySubject(domain, subjectClass, event);
      logger.debug("共找到 " + orders.size() + " 项。(" + logCtx + ")");
      if (orders.size() == 0)
        throw new NoneException();

      logger.debug("约束条件过滤……(" + logCtx + ")");
      orders = this.filterByConstraint(orders, aDict);
      logger.debug("符合条件的 " + orders.size() + " 项。(" + logCtx + ")");
      if (orders.size() == 0)
        throw new NoneException();

      logger.debug("产生备选提醒……(" + logCtx + ")");
      List<PromptRecord> promptRecords = this
          .generatePromptRecords(orders, aDict, pmptCtx, operate);
      logger.debug("备选提醒 " + promptRecords.size() + " 项。(" + logCtx + ")");
      if (promptRecords.size() == 0)
        throw new NoneException();

      logger.debug("备选提醒去重……(" + logCtx + ")");
      this.distinctPromptRecords(promptRecords);
      logger.debug("备选提醒 " + promptRecords.size() + " 项。(" + logCtx + ")");
      if (promptRecords.size() == 0)
        throw new NoneException();

      logger.debug("产生提醒……(" + logCtx + ")");
      this.putPrompts(promptRecords, operate);
      logger.debug("触发结束。共产生提醒 " + promptRecords + " 项。(" + logCtx + ")");

    } catch (NoneException e) {
      logger.debug("触发结束。共产生提醒 0 项。(" + logCtx + ")");
    } catch (BusinessException e) {
      logger.debug("触发过程中断。(" + logCtx + ")");
      throw e;
    } catch (HibernateException e) {
      logger.debug("触发过程中断。(" + logCtx + ")");
      throw e;
    }
  }

  private IDomainMgr domainMgr = null;
  private IMscbOrderMgr orderMgr = null;
  private IMscbCancelOrderMgr cancelOrderMgr = null;

  private IDomainMgr getDmMgr() throws BusinessException {
    if (domainMgr == null)
      domainMgr = (IDomainMgr) NamingMgr.getInstance().lookupMgr(IDomainMgr.class);
    return domainMgr;
  }

  private IMscbOrderMgr getOrdMgr() throws BusinessException {
    if (orderMgr == null)
      orderMgr = (IMscbOrderMgr) NamingMgr.getInstance().lookupMgr(IMscbOrderMgr.class);
    return orderMgr;
  }

  private IMscbCancelOrderMgr getCOrdMgr() throws BusinessException {
    if (cancelOrderMgr == null)
      cancelOrderMgr = (IMscbCancelOrderMgr) NamingMgr.getInstance().lookupMgr(
          IMscbCancelOrderMgr.class);
    return cancelOrderMgr;
  }

  private ClassifiedHashtable verifyToSave(BMscbOrder biz, IMscbOrder perz)
      throws BusinessException, HibernateException {
    ClassifiedHashtable h = this.checkOca(perz, biz.getOca(), CMscbOrder.CAPTION_CLASS, null);
    if (h != null)
      return h;

    h = new ClassifiedHashtable();

    if (biz.getSubjectClass() == null)
      h.putMessage("subject", ClassifiedHashtable.ERROR, CMscbOrder.CAPTION_CLASS + "的"
          + CMscbOrder.CAPTION_SUBJECT + "不可为空。");
    if (biz.getEvent() == null)
      h.putMessage("event", ClassifiedHashtable.ERROR, CMscbOrder.CAPTION_CLASS + "的"
          + CMscbOrder.CAPTION_EVENT + "不可为空。");
    if (biz.getSubscriber() == null)
      h.putMessage("subscriber", ClassifiedHashtable.ERROR, CMscbOrder.CAPTION_CLASS + "的"
          + CMscbOrder.CAPTION_SUBSCRIBER + "不可为空。");
    else {
      ISubscriberMgr subMgr = SubscriberMgrFactory.buildMgr(biz.getSubscriber().getMgrClassName());
      if (!subMgr.isValid(biz.getSubscriber()))
        h.putMessage("subscriber", ClassifiedHashtable.ERROR, CMscbOrder.CAPTION_CLASS + "的"
            + CMscbOrder.CAPTION_SUBSCRIBER + "无法验证其合法。");
    }

    if (biz.getPrompts() == null || biz.getPrompts().size() == 0)
      h.putMessage("prompts", ClassifiedHashtable.ERROR, CMscbOrder.CAPTION_CLASS + "中至少选择一种提醒方式。");
    else {
      Set<String> pvdList = new HashSet<String>();
      for (BMscbOrderPrompt bizPrompt : biz.getPrompts()) {
        String pvd = bizPrompt.getProviderClassName();
        if (pvdList.contains(pvd)) {
          h.putMessage("prompts", ClassifiedHashtable.ERROR, CMscbOrder.CAPTION_CLASS
              + "中不可重复选择提醒方式。");
          break;
        }
        pvdList.add(pvd);
      }
    }

    return h.size() > 0 ? h : null;
  }

  private void writeToOrder(BMscbOrder biz, IMscbOrder perz) throws BusinessException {
    perz.setSubjectClass(biz.getSubjectClass());
    perz.setEvent(biz.getEvent());
    perz.setConstraintContext(biz.getConstraintContext());
    perz.setConstraintClassName(biz.getConstraintClassName());
    perz.setConstraintDesc(biz.getConstraintDesc());
    perz.setSubscriber(biz.getSubscriber());
    perz.setNote(biz.getNote());
    perz.setFlag(biz.getFlag());

    CollectionMatcher<BMscbOrderPrompt, IMscbOrderPrompt> cm = new CollectionMatcher(
        new ElementMatcher<BMscbOrderPrompt, IMscbOrderPrompt>() {
          public boolean match(BMscbOrderPrompt bizPrompt, IMscbOrderPrompt perzPrompt)
              throws Exception {
            if (bizPrompt.getUuid() == null || perzPrompt.getUuid() == null)
              return false;
            return bizPrompt.getUuid().equals(perzPrompt.getUuid());
          }
        });
    try {
      List<IMscbOrderPrompt> pprompts = perz.getPrompts();
      if (pprompts == null) {
        pprompts = new ArrayList<IMscbOrderPrompt>();
      }
      Collection<IMscbOrderPrompt> delCol = cm.subtractB(pprompts, biz.getPrompts());
      for (IMscbOrderPrompt perzPrompt : delCol)
        this.getOrdMgr().removePromptFromOrder(perz, perzPrompt);

      Map<BMscbOrderPrompt, IMscbOrderPrompt> modMap = cm.multiple(biz.getPrompts(), pprompts);
      for (BMscbOrderPrompt bizPrompt : modMap.keySet()) {
        IMscbOrderPrompt perzPrompt = modMap.get(bizPrompt);
        this.writeToPrompt(bizPrompt, perzPrompt);
      }

      Collection<BMscbOrderPrompt> insCol = cm.subtractA(biz.getPrompts(), pprompts);
      for (BMscbOrderPrompt bizPrompt : insCol) {
        IMscbOrderPrompt perzPrompt = this.getOrdMgr().createPrompt(perz);
        this.writeToPrompt(bizPrompt, perzPrompt);
        this.getOrdMgr().addPromptToOrder(perz, perzPrompt);
      }
    } catch (Exception e) {
      throw new BusinessException(e);
    }
  }

  private void writeToPrompt(BMscbOrderPrompt biz, IMscbOrderPrompt perz) throws BusinessException {
    perz.setProviderClassName(biz.getProviderClassName());
    perz.setContext(biz.getContext());
  }

  private ClassifiedHashtable verifyToCancel(BMscbOrder biz, IMscbOrder perz)
      throws BusinessException {
    ClassifiedHashtable h = this.checkOca(perz, biz.getOca(), CMscbOrder.CAPTION_CLASS, null);
    if (h != null)
      return h;

    h = new ClassifiedHashtable();

    if (CMscbOrder.VALUE_STATE_DELETED.equals(perz.getState()))
      h.putMessage("order", ClassifiedHashtable.ERROR, "指定的" + CMscbOrder.CAPTION_CLASS
          + "已经被取消，无法再次取消。");

    return h.size() > 0 ? h : null;
  }

  private void deleteOrder(IMscbOrder order, OperateContext operate) throws BusinessException,
      HibernateException {
    assert order != null;
    assert operate != null;

    // 同时删除对应的取消订阅单。
    List<IMscbCancelOrder> cancelOrders = this.getCOrdMgr().getOrdersByCancelled(order);
    for (IMscbCancelOrder cancelOrder : cancelOrders)
      this.getCOrdMgr().deleteOrder(cancelOrder, operate);

    this.getOrdMgr().deleteOrder(order, operate);
  }

  private void createAndSaveCancelOrder(IMscbOrder cancelledOrder, Subscriber subscriber,
      OperateContext operate) throws BusinessException, HibernateException {
    assert cancelledOrder != null;
    assert subscriber != null;
    assert operate != null;

    if (this.getCOrdMgr().getValidOrderByKey(cancelledOrder, subscriber) != null)
      return;

    IMscbCancelOrder cancelOrder = this.getCOrdMgr().createOrder(cancelledOrder.getDomain());
    cancelOrder.setCancelledOrder(cancelledOrder);
    cancelOrder.setSubscriber(subscriber);
    this.getCOrdMgr().saveOrder(cancelOrder, operate);
  }

  private String getLogContext(String domainUuid, String subjectClass, String event) {
    assert domainUuid != null;
    assert subjectClass != null;
    assert event != null;

    StringBuffer sb = new StringBuffer();
    sb.append("domainUuid=" + domainUuid + ";");
    sb.append("subjectClass=" + subjectClass + ";");
    sb.append("event=" + event + ";");
    return sb.toString();
  }

  /** 逐一进行约束条件检查，取得有效的订阅单列表。 */
  private List<IMscbOrder> filterByConstraint(List<IMscbOrder> orders, Map<String, Object> dict)
      throws BusinessException, HibernateException {
    assert orders != null;
    assert dict != null;

    List<IMscbOrder> retList = new ArrayList<IMscbOrder>();
    for (IMscbOrder order : orders) {
      String cstClassName = order.getConstraintClassName();
      if (cstClassName != null) {
        IConstraint cst = ConstraintFactory.buildConstraint(cstClassName);
        cst.setContext(order.getConstraintContext());
        cst.setDict(dict);
        if (!cst.isValid())
          continue;
      }
      retList.add(order);
    }
    return retList;
  }

  /** 对每一个订阅单，对订阅人（角色转换为用户，除去已取消的）产生提醒。 */
  private List<PromptRecord> generatePromptRecords(List<IMscbOrder> orders,
      Map<String, Object> dict, IPromptContent pmptCtx, OperateContext operate)
      throws BusinessException, HibernateException {
    assert orders != null;
    assert dict != null;
    assert operate != null;

    List<PromptRecord> promptRecords = new ArrayList<PromptRecord>();

    for (IMscbOrder order : orders) {
      Subscriber subscriber = order.getSubscriber();
      if (subscriber == null)
        continue;
      ISubscriberMgr subMgr = SubscriberMgrFactory.buildMgr(subscriber.getMgrClassName());
      if (!subMgr.isValid(subscriber))
        continue;
      // Role -> User
      List<Subscriber> users = null;
      if (CSubscriber.VALUE_TYPE_USER.equals(subscriber.getType_())) {
        users = new ArrayList<Subscriber>();
        users.add(subscriber);
      } else {
        users = subMgr.getUsersByRole(subscriber);
        // 排除已经取消的
        List<IMscbCancelOrder> corders = this.getCOrdMgr().getOrdersByCancelled(order);
        for (IMscbCancelOrder corder : corders) {
          if (CMscbCancelOrder.VALUE_STATE_DELETED.equals(corder))
            continue;
          Subscriber cuser = corder.getSubscriber();
          for (Subscriber user : users)
            if (user.equals(cuser)) {
              users.remove(user);
              break;
            }
        }
      }
      // 每一个用户+提醒方式产生一条PromptRecord
      for (Subscriber user : users) {
        for (IMscbOrderPrompt prompt : order.getPrompts()) {
          PromptRecord record = new PromptRecord();
          record.receiver = user;
          IPromptProvider pmptPvd = PromptProviderFactory.getInstance().getProvider(
              prompt.getProviderClassName());
          if (pmptPvd == null)
            continue;
          record.pmptPvd = pmptPvd;
          IPromptContent content = pmptPvd.createContent(prompt.getContext());
          content.setTime(operate.getTime()); // 必须在merge之前。确保通过pmptCtx参数传入的time值优先。
          if (pmptCtx != null)
            content.merge(pmptCtx);
          content.applyParams(dict);
          content.setSenderId(order.getUuid());
          content.setSenderClassName(IMscbOrder.class.getName());
          content.setText(order.getNote());
          content.setFlag(order.getFlag());
          record.pmptCtn = content;
          promptRecords.add(record);
        }
      }
    }
    return promptRecords;
  }

  /** 在所有提醒列表范围内去重。 */
  private void distinctPromptRecords(List<PromptRecord> promptRecords) throws BusinessException {
    assert promptRecords != null;

    for (int i = 0; i < promptRecords.size(); i++) {
      PromptRecord record1 = promptRecords.get(i);
      int j = promptRecords.size() - 1;
      while (j > i) {
        PromptRecord record2 = promptRecords.get(j);
        if (record1.sameAs(record2))
          promptRecords.remove(record2);
        j--;
      }
    }
  }

  /** 发出提醒。 */
  private void putPrompts(List<PromptRecord> promptRecords, OperateContext operate)
      throws BusinessException, HibernateException {
    assert promptRecords != null;
    assert operate != null;

    for (PromptRecord record : promptRecords) {
      Subscriber sub = record.receiver;
      ISubscriberMgr subMgr = SubscriberMgrFactory.buildMgr(sub.getMgrClassName());
      Object receiver = subMgr.toNature(sub);
      record.pmptPvd.putToUserT(receiver, record.pmptCtn, operate);
    }
  }

  /**
   * 提醒记录
   * 
   * @author lxm
   * 
   */
  private class PromptRecord {
    /** 提醒接收人 */
    public Subscriber receiver;
    /** 提醒内容 */
    public IPromptContent pmptCtn;
    /** 提醒提供者 */
    public IPromptProvider pmptPvd;

    public boolean sameAs(PromptRecord target) throws BusinessException {
      return this.receiver.equals(target.receiver) && this.pmptCtn.sameAs(target.pmptCtn)
          && this.pmptPvd.equals(target.pmptPvd);
    }
  }

}
